package io.gitee.mayan50.autoassociate.value;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import io.gitee.mayan50.autoassociate.annotation.OneToMany;
import io.gitee.mayan50.autoassociate.annotation.OneToOne;
import io.gitee.mayan50.autoassociate.constant.AssociateTypeEnum;
import io.gitee.mayan50.autoassociate.util.StrUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 列表变量关联实现实体类
 */
public class AssociateFieldList<E> {

    private final AssociateTypeEnum type;

    private OneToOne oneToOne;

    private OneToMany oneToMany;

    private final BaseMapper<E> baseMapper;

    private final Map<Object, Integer> valueCount;

    private Map<Object, E> entityMap;

    private Map<Object, Collection<E>> entityListMap;

    private final Map<Integer, Object> indexValueMap;

    public AssociateFieldList(OneToOne oneToOne, BaseMapper<E> baseMapper) {
        this.type = AssociateTypeEnum.ONE_TO_ONE;
        this.oneToOne = oneToOne;
        this.baseMapper = baseMapper;
        this.valueCount = new HashMap<>();
        this.indexValueMap = new HashMap<>();
    }

    public AssociateFieldList(OneToMany oneToMany, BaseMapper<E> baseMapper) {
        this.type = AssociateTypeEnum.ONE_TO_MANY;
        this.oneToMany = oneToMany;
        this.baseMapper = baseMapper;
        this.valueCount = new HashMap<>();
        this.indexValueMap = new HashMap<>();
    }

    public void put(Object value, int index) {
        if (valueCount.containsKey(value)) valueCount.put(value, valueCount.get(value) + 1);
        else valueCount.put(value, 1);
        this.indexValueMap.put(index, value);
    }

    public boolean isOneToOne() {
        return this.type == AssociateTypeEnum.ONE_TO_ONE;
    }

    public String column() {
        if (isOneToOne()) return oneToOne.column();
        return oneToMany.column();
    }

    public String sql() {
        if (isOneToOne()) return oneToOne.sql();
        return oneToMany.sql();
    }

    public int deep() {
        if (isOneToOne()) return oneToOne.deep();
        return oneToMany.deep();
    }

    public String[] condition() {
        if (isOneToOne()) return oneToOne.condition();
        return oneToMany.condition();
    }

    public void initEntityMap() {
        this.entityMap = new HashMap<>();
        List<E> es = baseMapper.selectList(new QueryWrapper<E>().in(oneToOne.associateColumn(), valueCount.keySet()));
        if (es == null || es.isEmpty()) return;
        Function<E, Object> function = e -> {
            try {
                Field field = e.getClass().getDeclaredField(StrUtils.humpToLine(oneToOne.associateColumn()));
                field.setAccessible(true);
                return field.get(e);
            } catch (IllegalAccessException | NoSuchFieldException ex) {
                throw new RuntimeException(ex);
            }
        };
        entityMap = es.stream().collect(Collectors.toMap(function, Function.identity()));
    }

    public <T> void setFieldByOneToOne(T t, Field field, int index) throws IllegalAccessException {
        if (this.entityMap == null) initEntityMap();
        field.set(t, this.entityMap.get(indexValueMap.get(index)));
    }

    public void initEntityListMap() {
        this.entityListMap = new HashMap<>();
        for (Object o : this.valueCount.keySet()) this.entityListMap.put(o, baseMapper.selectList(new QueryWrapper<E>().eq(oneToMany.associateColumn(), o)));
    }

    public <T> void setFieldByOneToMany(T t, Field field, int index) throws IllegalAccessException {
        if (this.entityListMap == null) initEntityListMap();
        field.set(t, this.entityListMap.get(indexValueMap.get(index)));
    }

    public <T> void setField(T t, Field field, int index) throws IllegalAccessException {
        if (isOneToOne()) setFieldByOneToOne(t, field, index);
        else setFieldByOneToMany(t, field, index);
    }

}
